package us.versus.them.stickler;

import us.versus.them.stickler.models.Original;
import us.versus.them.stickler.graphics.ThinGraphics;
import us.versus.them.stickler.graphics.FlashGraphics;
import us.versus.them.stickler.graphics.JsGraphics;

class App {

	var flooba : ThinGraphics;

	var vertlets : Array< Vertlet >; 
	var limbs    : Array< Limb >;
	var gravity  : Point;          

	var timeStep : Float;
	var damping : Float;
	var iterations : Int;

	var size : Point;

	var classic : Bool;
	var buttonSize : Float;
	var jointSize : Float;
	var selected : Vertlet;

	public function new() {
#if flash
		flooba = new FlashGraphics();
#end
#if js
		flooba = new JsGraphics( 'stickler', 10 );
#end

		this.reset();
		this.loadModel();

		flooba.addEnterFrame( loop );
		flooba.addMouseDown( mouseDown );
		flooba.addMouseUp( mouseUp );
		flooba.addMouseMove( mouseMove );
	}

	public function reset() {
		this.timeStep = 1;
		this.damping = 0.95;
		this.iterations = 2;
		this.classic = true;

		this.size = new Point( 1024, 1024, 1024 );
		this.buttonSize = 32;
		this.jointSize = this.buttonSize;

		this.gravity = new Point( 0, 2, 0 );
		this.selected = null;

		this.vertlets = new Array< Vertlet >();
		this.limbs = new Array< Limb >();
	}

	public function loadModel() {
		this.gravity = new Point( 0, 0, 0 );
		( new Original() ).join( this , new Point( 0, 0,   0 ), 1 );
	}

	public function add( vertlet : Vertlet ) {
		this.vertlets.push( vertlet );
	}

	public function link( start : Vertlet, stop : Vertlet ) {
		this.limbs.push( new Limb( start, stop ) );
	}

	public function loop() {
		flooba.clear();

		// this is really cheesy
		this.drawButtons();

		for ( vertlet in vertlets ) {
			this.drawPoint( vertlet.current );
		}

		for ( limb in limbs ) {
			this.drawLimb( limb.start.current , limb.stop.current );
		}

		this.move();
	}

	public function drawButtons() {
		var point : Point = new Point();
		var size : Point = new Point( this.jointSize, buttonSize, buttonSize );

		Point.assign( point, buttonSize * 0, 0 );
		flooba.drawBox( point, size, 0xFFDDDD );

		Point.assign( point, buttonSize * 1, 0 );
		flooba.drawBox( point, size, 0xDDFFDD );

		Point.assign( point, buttonSize * 2, 0 );
		flooba.drawBox( point, size, 0xFF0000 ); 

		Point.assign( point, buttonSize * 3, 0 );
		flooba.drawBox( point, size, 0x0000FF );
	}

	public function drawPoint( point : Point ) {
		var size : Point = new Point( this.jointSize, this.jointSize, this.jointSize );
		flooba.drawBox( point, size, 0xFFDDFF );
	}

	public function drawLimb( start : Point, stop : Point ) {
		flooba.drawLine( start, stop );
	}

	public function move() {
		accumulateForces();
		verlet();
		satisfyConstraints();
	}

	private function accumulateForces() {
		for ( vertlet in this.vertlets ) {
			Vertlet.accumulateForces( vertlet, this.gravity );
		}
	}

	private function verlet() {
		for ( vertlet in this.vertlets ) {
			vertlet.vertlet( this.timeStep, this.damping );
		}
	}

	private function satisfyConstraints() {
		for ( i in 0...iterations ) {

			for ( vertlet in vertlets ) {
				Point.bounds( vertlet.current, this.size );
			}

			for ( limb in limbs ) {
				limb.satisfy();
			}
		}
	}

	/// this is pretty lame...

	public function button( x : Float, y : Float ) : Bool {
		var click : Bool = false;
		if ( y < this.buttonSize ) {
			if ( x < this.buttonSize ) {
				this.gravity.y -= 0.5;
				click = true;
			} else
			if ( x < this.buttonSize * 2 ) {
				this.gravity.y += 0.5;
				click = true;
			} else
			if ( x < this.buttonSize * 3 ) {
				this.reset();
				this.loadModel();
				click = true;
			} else
			if ( x < this.buttonSize * 4 ) {
				this.classic = !this.classic;
				if ( this.classic ) {
					this.jointSize *= 2;
				} else {
					this.jointSize /= 2;
				}
				click = true;
			} 
		}
		return click;
	}

	public function select( x : Float, y : Float ) : Bool {
		this.selected = null;
		for ( vertlet in vertlets ) {
			if ( true
				&& x > vertlet.current.x - jointSize / 2
				&& x < vertlet.current.x + jointSize / 2
				&& y > vertlet.current.y - jointSize / 2
				&& y < vertlet.current.y + jointSize / 2
			) {
				this.selected = vertlet;
				break;
			}
		}
		return null != this.selected;
	}

	public function fire( x : Float, y : Float ) : Bool {
		if ( !this.classic ) {
			Point.assign( 
				( new Original() ).join( 
					this 
					, new Point( -x, -y, 0 )
					, 0.2 
				).current
				, 0, this.size.y * 2, 0
			);
		}
		return true;
	}

	public function mouseDown( x : Int, y : Int ) {
		( this.button( x, y ) || this.select( x, y ) || this.fire( x, y ) );
	}

	public function mouseUp( x : Int, y : Int ) {
		this.selected = null;
	}

	public function mouseMove( x : Int, y : Int ) {
		if ( null != this.selected ) {
			this.selected.current.x = x;
			this.selected.current.y = y;
		}
	}
}
